#include "device.h"
#include "softiic.h"
#include "peripheral.h"
#define SOFT_IIC_LOG(format, ...) //__OSAL_LOG("[softiic.c] " C_GREEN format C_NONE "\r\n", ##__VA_ARGS__)
#define IO_IICA_SDA_PIN IIC_SDA_PIN//17
#define IO_IICA_SCL_PIN IIC_SCL_PIN//18
#define IO_IICB_SDA_PIN NOUSE//
#define IO_IICB_SCL_PIN NOUSE//
const softiic_config_t iicCfgA = {IO_IICA_SCL_PIN, IO_IICA_SDA_PIN, 20};
const softiic_config_t iicCfgB = {IO_IICB_SCL_PIN, IO_IICB_SDA_PIN, 20};
// IO操作
#define SOFT_IIC_SDA_OUT gpio_set_direction(BITMASK(cfg->sdaPin), GPIO_OUTPUT)
#define SOFT_IIC_SDA_IN gpio_set_direction(BITMASK(cfg->sdaPin), GPIO_INPUT)

#define SOFT_IIC_SCL_OUT gpio_set_direction(BITMASK(cfg->sclPin), GPIO_OUTPUT)

#define SOFT_IIC_SDA_L gpio_write(BITMASK(cfg->sdaPin), GPIO_LOW)
#define SOFT_IIC_SDA_H gpio_write(BITMASK(cfg->sdaPin), GPIO_HIGH)

#define SOFT_IIC_SCL_L gpio_write(BITMASK(cfg->sclPin), GPIO_LOW)
#define SOFT_IIC_SCL_H gpio_write(BITMASK(cfg->sclPin), GPIO_HIGH)

#define SOFT_IIC_READ_SDA gpio_read(BITMASK(cfg->sdaPin))

#define IIC_DELAY  {uint16_t k = 0;	for (k = 0; k < cfg->delyCounter; k++)__NOP();}

#define I2C_WR 0 /* 写控制bit */
#define I2C_RD 1 /* 读控制bit */

/*
*********************************************************************************************************
*	函 数 名: i2c_Start
*	功能说明: CPU发起I2C总线启动信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Start(softiic_config_t *cfg)
{
	/* 当SCL高电平时，SDA出现一个下跳沿表示I2C总线启动信号 */
	SOFT_IIC_SDA_H;
	SOFT_IIC_SCL_H;
	IIC_DELAY;
	SOFT_IIC_SDA_L;
	IIC_DELAY;
	SOFT_IIC_SCL_L;
	// IIC_DELAY;
}

/*
*********************************************************************************************************
*	函 数 名: i2c_Stop
*	功能说明: CPU发起I2C总线停止信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Stop(softiic_config_t *cfg)
{
	/* 当SCL高电平时，SDA出现一个上跳沿表示I2C总线停止信号 */
	SOFT_IIC_SDA_L;
	SOFT_IIC_SCL_H;
	IIC_DELAY;
	SOFT_IIC_SDA_H;
}
/*
*********************************************************************************************************
*	函 数 名: i2c_Ack
*	功能说明: CPU产生一个ACK信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(softiic_config_t *cfg)
{
	SOFT_IIC_SDA_L; /* CPU驱动SDA = 0 */
	IIC_DELAY;
	SOFT_IIC_SCL_H; /* CPU产生1个时钟 */
	IIC_DELAY;
	SOFT_IIC_SCL_L;
}

/*
*********************************************************************************************************
*	函 数 名: i2c_NAck
*	功能说明: CPU产生1个NACK信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(softiic_config_t *cfg)
{
	SOFT_IIC_SDA_H; /* CPU驱动SDA = 1 */
	IIC_DELAY;
	SOFT_IIC_SCL_H; /* CPU产生1个时钟 */
	IIC_DELAY;
	SOFT_IIC_SCL_L;
	IIC_DELAY;
}
/*
*********************************************************************************************************
*	函 数 名: i2c_SendByte
*	功能说明: CPU向I2C总线设备发送8bit数据
*	形    参：_ucByte ： 等待发送的字节
*	返 回 值: 无
*********************************************************************************************************
*/
static void i2c_SendByte(softiic_config_t *cfg, uint8_t _ucByte)
{
	uint8_t i;
	Device_EnterCritical();
	/* 先发送字节的高位bit7 */
	for (i = 0; i < 8; i++)
	{
		if (_ucByte & 0x80)
		{
			SOFT_IIC_SDA_H;
		}
		else
		{
			SOFT_IIC_SDA_L;
		}

		IIC_DELAY;
		SOFT_IIC_SCL_H;
		IIC_DELAY;
		SOFT_IIC_SCL_L;

		if (i == 7)
		{
			SOFT_IIC_SDA_H; // 释放总线
			IIC_DELAY;
		}
		_ucByte <<= 1; /* 左移一个bit */
	}
	Device_ExitCritical();
}
/*
*********************************************************************************************************
*	函 数 名: i2c_ReadByte
*	功能说明: CPU从I2C总线设备读取8bit数据
*	形    参：无
*	返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(softiic_config_t *cfg)
{
	uint8_t i;
	uint8_t value = 0;
	Device_EnterCritical();
	/* 读到第1个bit为数据的bit7 */
	SOFT_IIC_SDA_IN;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		SOFT_IIC_SCL_H;

		IIC_DELAY;
		if (SOFT_IIC_READ_SDA)
		{
			value++;
		}
		SOFT_IIC_SCL_L;
		IIC_DELAY;
	}
	SOFT_IIC_SDA_OUT;
	Device_ExitCritical();
	return value;
}
/*
*********************************************************************************************************
*	函 数 名: i2c_WaitAck
*	功能说明: CPU产生一个时钟，并读取器件的ACK应答信号
*	形    参：无
*	返 回 值: 返回0表示正确应答，1表示无器件响应
*********************************************************************************************************
*/
static uint8_t i2c_WaitAck(softiic_config_t *cfg)
{
	uint8_t re;

	SOFT_IIC_SDA_H; /* CPU释放SDA总线 */
	IIC_DELAY;
	SOFT_IIC_SCL_H; /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	SOFT_IIC_SDA_IN;
	IIC_DELAY;

	if (SOFT_IIC_READ_SDA) /* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	SOFT_IIC_SDA_OUT;
	SOFT_IIC_SCL_L;
	IIC_DELAY;
	return re;
}

/*
*********************************************************************************************************
*	函 数 名: Softiic_Write
*	功能说明: 向串行EEPROM指定地址写入若干数据，采用页写操作提高写入效率
*	形    参：_usAddress : 起始地址
*			 _usSize : 数据长度，单位为字节
*			 _pWriteBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败，1表示成功
*********************************************************************************************************
*/
uint8_t Softiic_Write(uint8_t channel, uint8_t slave_add, uint8_t *Buf, uint8_t Size)
{
	uint16_t i, m;
	uint16_t usAddr;
	softiic_config_t *cfg;
	if (channel == 1)
	{
		cfg = &iicCfgA;
	}
	else
	{
		cfg = &iicCfgB;
	}
	for (i = 0; i < Size; i++)
	{
		/* 当发送第1个字节或是页面首地址时，需要重新发起启动信号和地址 */
		if (i == 0)
		{
			/*　第０步：发停止信号，启动内部写操作　*/
			// i2c_Stop(cfg);

			/* 通过检查器件应答的方式，判断内部写操作是否完成, 一般小于 10ms
				CLK频率为200KHz时，查询次数为30次左右
			*/
			for (m = 0; m < 100; m++)
			{
				/* 第1步：发起I2C总线启动信号 */
				i2c_Start(cfg);

				/* 第2步：发起控制字节，高7bit是地址，bit0是读写控制位，0表示写，1表示读 */
				i2c_SendByte(cfg, slave_add | I2C_WR); /* 此处是写指令 */

				/* 第3步：发送一个时钟，判断器件是否正确应答 */
				if (i2c_WaitAck(cfg) == 0)
				{
					break;
				}
			}

			if (m == 100)
			{
				goto cmd_fail; /* EEPROM器件写超时 */
			}
		}

		/* 第6步：开始写入数据 */
		i2c_SendByte(cfg, Buf[i]);

		/* 第7步：发送ACK */
		if (i2c_WaitAck(cfg) != 0)
		{
			goto cmd_fail; /* EEPROM器件无应答 */
		}

		usAddr++; /* 地址增1 */
	}

	/* 命令执行成功，发送I2C总线停止信号 */
	i2c_Stop(cfg);
	return 1;

cmd_fail: /* 命令执行失败后，切记发送停止信号，避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	i2c_Stop(cfg);
	return 0;
}
/*
*********************************************************************************************************
*	函 数 名: Softiic_Read
*	功能说明: 从串行EEPROM指定地址处开始读取若干数据
*	形    参：_usAddress : 起始地址
*			 _usSize : 数据长度，单位为字节
*			 _pReadBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败，1表示成功
*********************************************************************************************************
*/
uint8_t Softiic_Read(uint8_t channel, uint8_t slave_add, uint8_t *_pReadBuf, uint8_t _usSize)
{
	uint16_t i;
	softiic_config_t *cfg;
	if (channel == 1)
	{
		cfg = &iicCfgA;
	}
	else
	{
		cfg = &iicCfgB;
	}
	SOFT_IIC_LOG("Softiic_Read slave_add:%02X,data[0]:%02X,len:%d", slave_add, _pReadBuf[0], _usSize);
	/* 采用串行EEPROM随即读取指令序列，连续读取若干字节 */

	/* 第1步：发起I2C总线启动信号 */
	i2c_Start(cfg);

	/* 第7步：发起控制字节，高7bit是地址，bit0是读写控制位，0表示写，1表示读 */
	i2c_SendByte(cfg, slave_add | I2C_RD); /* 此处是读指令 */

	/* 第8步：发送ACK */
	if (i2c_WaitAck(cfg) != 0)
	{

		goto cmd_fail; /* EEPROM器件无应答 */
	}

	/* 第9步：循环读取数据 */
	for (i = 0; i < _usSize; i++)
	{
		_pReadBuf[i] = i2c_ReadByte(cfg); /* 读1个字节 */

		/* 每读完1个字节后，需要发送Ack， 最后一个字节不需要Ack，发Nack */
		if (i != _usSize - 1)
		{
			i2c_Ack(cfg); /* 中间字节读完后，CPU产生ACK信号(驱动SDA = 0) */
			SOFT_IIC_LOG("i2c_Ack");
		}
		else
		{
			i2c_NAck(cfg); /* 最后1个字节读完后，CPU产生NACK信号(驱动SDA = 1) */
			SOFT_IIC_LOG("i2c_NAck");
		}
	}
	/* 发送I2C总线停止信号 */
	i2c_Stop(cfg);
	return 1; /* 执行成功 */

cmd_fail: /* 命令执行失败后，切记发送停止信号，避免影响I2C总线上其他设备 */
		  /* 发送I2C总线停止信号 */
	SOFT_IIC_LOG("Softiic_Read i2c_WaitAck err");
	i2c_Stop(cfg);
	return 0;
}

void Softiic_init(uint8_t channel)
{
	softiic_config_t *cfg;
	if (channel == 1)
	{
		cfg = &iicCfgA;
	}
	else
	{
		cfg = &iicCfgB;
	}
	SOFT_IIC_LOG("Softiic_init");
	pinmux_config(cfg->sclPin, PINMUX_GPIO_MODE_CFG);
	pinmux_config(cfg->sdaPin, PINMUX_GPIO_MODE_CFG);
	gpio_set_direction((1 << cfg->sdaPin) | (1 << cfg->sclPin), GPIO_OUTPUT);
	pmu_pin_mode_set((1 << cfg->sdaPin) | (1 << cfg->sclPin), PMU_PIN_MODE_OD_PU);
}


void Softiic_deinit(uint8_t channel)
{
	softiic_config_t *cfg;
	if (channel == 1)
	{
		cfg = &iicCfgA;
	}
	else
	{
		cfg = &iicCfgB;
	}
	// SOFT_IIC_LOG("Softiic_deinit");
	pinmux_config(cfg->sclPin, PINMUX_GPIO_MODE_CFG);
	pinmux_config(cfg->sdaPin, PINMUX_GPIO_MODE_CFG);
	gpio_write((1 << cfg->sdaPin) | (1 << cfg->sclPin), GPIO_HIGH);
	gpio_set_direction((1 << cfg->sdaPin) | (1 << cfg->sclPin), GPIO_OUTPUT);
	// pmu_pin_mode_set((1 << cfg->sdaPin) | (1 << cfg->sclPin), PMU_PIN_MODE_OD_PU);

}
